/**
 * @author: 子龙
 */
package com.fzl.lotterysystem.service.impl;

import com.fzl.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.fzl.lotterysystem.common.utils.JacksonUtil;
import com.fzl.lotterysystem.common.utils.RedisUtil;
import com.fzl.lotterysystem.controller.param.DrawPrizeParam;
import com.fzl.lotterysystem.controller.param.ShowWinningRecordsParam;
import com.fzl.lotterysystem.dao.dataobject.*;
import com.fzl.lotterysystem.dao.mapper.*;
import com.fzl.lotterysystem.service.DTO.WinningRecordDTO;
import com.fzl.lotterysystem.service.DrawPrizeService;
import com.fzl.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.fzl.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.fzl.lotterysystem.service.enums.ActivityStatusEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.fzl.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.fzl.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

@Slf4j
@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {

    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";
    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PrizeMapper prizeMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 生产者发消息
     * @param drawPrizeParam
     */
    @Override
    public void drawPrize(DrawPrizeParam drawPrizeParam) {

        Map<String, String> map = new HashMap<>();
        map.put("messageId",String.valueOf(UUID.randomUUID()));
        map.put("messageData", JacksonUtil.writeValueAsString(drawPrizeParam));

        //发消息:交换机，队列，绑定的key   就知道发给哪个队列了
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING,map);

        log.info("消息发送成功,map={}",JacksonUtil.writeValueAsString(drawPrizeParam));
    }

    /**
     * 校验数据
     *
     * @param drawPrizeParam
     * @return
     */
    @Override
    public boolean checkDrawPrizeParam(DrawPrizeParam drawPrizeParam) {

        ActivityDO activityDO=activityMapper.selectById(drawPrizeParam.getActivityId());
        //奖品是否存在可以从ActivityPrize表中查找，因为保存这个表的时候，已经校验了奖品存在
        ActivityPrizeDO activityPrizeDO=activityPrizeMapper.selectByAPId(
                drawPrizeParam.getActivityId(),drawPrizeParam.getPrizeId());


        // 活动或奖品是否存在
        if (null == activityDO || null == activityPrizeDO) {
            // throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            log.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY.getMessage());
            return false;
        }

        // 活动是否有效
        if (activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            // throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            log.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_COMPLETED.getMessage());
            return false;
        }

        // 奖品是否有效
        if (activityPrizeDO.getStatus()
                .equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            // throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            log.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED.getMessage());
            return false;
        }

        // 中奖者人数是否和设置奖品数量一致 3 2
        if (activityPrizeDO.getPrizeAmount() != drawPrizeParam.getWinnerList().size()) {
            // throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            log.info("校验抽奖请求失败！失败原因：{}",
                    ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR.getMessage());
            return false;
        }
        return true;
    }

    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //查询活动信息奖品人员。。
        ActivityDO activityDO= activityMapper.selectById(param.getActivityId());
        List<UserDO> userDOList= userMapper.batchSelectByIds(param.getWinnerList()
                .stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList())
        );
        PrizeDO prizeDO= prizeMapper.selectById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO= activityPrizeMapper.selectByAPId(param.getActivityId(),param.getPrizeId());

        //构造整合记录--》保存
        List<WinningRecordDO> winningRecordDOList=userDOList
                .stream()
                .map(userDO ->{
                    WinningRecordDO recordDO=new WinningRecordDO();
                    recordDO.setActivityId(activityDO.getId());
                    recordDO.setActivityName(activityDO.getActivityName());
                    recordDO.setPrizeId(prizeDO.getId());
                    recordDO.setPrizeName(prizeDO.getName());
                    recordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    recordDO.setWinnerId(userDO.getId());
                    recordDO.setWinnerName(userDO.getUserName());
                    recordDO.setWinnerEmail(userDO.getEmail());
                    recordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    recordDO.setWinningTime(param.getWinningTime());

                    return recordDO;

                }
                ).collect(Collectors.toList());

        winningRecordMapper.batchInsert(winningRecordDOList);


        //缓存中奖者记录
        //1.缓存奖品维度记录（key：WinningRecord_activity_PrizeId，value：winningRecordDOList）
        cacheWinningRecords(param.getActivityId()+"_"+param.getPrizeId(),
                winningRecordDOList,
                WINNING_RECORDS_TIMEOUT);

        //2.缓存活动维度（key：WinningRecord_activityId，value：winningRecordDOList）
        //所有奖品都被抽完，活动才完成，这是才去查
        if (activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())){
            //查询活动维度的全量中将记录
            List<WinningRecordDO> allList= winningRecordMapper.selectByActivityId(param.getActivityId());

            cacheWinningRecords(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);
        }
        return winningRecordDOList;
    }

    /**
     * 删除中奖表和缓存的数据
     * 缓存有两条：奖品维度和活动维度
     * @param activityId
     * @param
     */
    @Override
    public void deleteRecords(Long activityId, Long prizeId) {
        if(activityId==null){
            log.warn("要删除的中奖记录相关的id为空");
            return;
        }

        //删除数据表
        winningRecordMapper.deleteRecords(activityId,prizeId);

        //删除缓存
        if(prizeId!=null){
            deleteWinnerRecords(activityId+"_"+prizeId);
        }

        // 无论是否传递了prizeId，都需要删除活动维度的中奖记录缓存：
        // 如果传递了prizeId, 证明奖品未抽奖，必须删除活动维度的缓存记录
        // 如果没有传递prizeId，就只是删除活动维度的信息
        deleteWinnerRecords(String.valueOf(activityId));
    }

    /**
     * 查询中奖记录
     * @param param
     * @return
     */
    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        //先查redis:奖品+活动维度
        String key = null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId()+"_"+param.getPrizeId();
        List<WinningRecordDO> winningRecordDOList= getWinningRecords(key);

        if (!CollectionUtils.isEmpty(winningRecordDOList)) {
            return convertToWinningRecordDTOList(winningRecordDOList) ;
        }

        //redis不存在，查库
        winningRecordDOList=winningRecordMapper.selectByActivityIdOrPrizeId(param.getActivityId(),param.getPrizeId());

        //存redis
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            log.info("查询中奖记录为空，param:{}",JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }

        cacheWinningRecords(key,winningRecordDOList,WINNING_RECORDS_TIMEOUT);

        return convertToWinningRecordDTOList(winningRecordDOList);

    }

    private List<WinningRecordDTO> convertToWinningRecordDTOList(
            List<WinningRecordDO> winningRecordDOList) {
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            return Arrays.asList();
        }
        return winningRecordDOList.stream()
                .map(winningRecordDO ->{
                    WinningRecordDTO winningRecordDTO=new WinningRecordDTO();
                    winningRecordDTO.setWinnerId(winningRecordDO.getWinnerId());
                    winningRecordDTO.setWinnerName(winningRecordDO.getWinnerName());
                    winningRecordDTO.setPrizeName(winningRecordDO.getPrizeName());
                    winningRecordDTO.setPrizeTier(ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
                    winningRecordDTO.setWinningTime(winningRecordDO.getWinningTime());

                    return winningRecordDTO;
                }).collect(Collectors.toList());
    }

    /**
     * 从缓存中删除中奖记录
     */
    private void deleteWinnerRecords(String key) {
        try {
            if (redisUtil.hasKey(WINNING_RECORDS_PREFIX + key)) {
                // 存在再删除
                redisUtil.del(WINNING_RECORDS_PREFIX + key);
            }
        } catch (Exception e) {
            log.error("删除中奖记录缓存异常，key:{}", key);
        }
    }

    /**
     * 缓存中将记录
     * @param key
     * @param winningRecordDOList
     * @param time
     */
    private void cacheWinningRecords(String key,
                                     List<WinningRecordDO> winningRecordDOList,
                                     Long time) {
        String str="";
        try {
            if(!StringUtils.hasText(key)|| CollectionUtils.isEmpty(winningRecordDOList)){
                log.warn("要缓存的内容为空！key:{}, value:{}",
                        key, JacksonUtil.writeValueAsString(winningRecordDOList));
                return;
            }
            str=JacksonUtil.writeValueAsString(winningRecordDOList);
                    redisUtil.set(WINNING_RECORDS_PREFIX+key, str, time);
        }catch (Exception e){
            log.error("缓存中奖记录异常！key:{}, value:{}", WINNING_RECORDS_PREFIX + key, str);

        }
    }

    private List<WinningRecordDO> getWinningRecords(String key) {
        try {
            if (!StringUtils.hasText(key)) {
                log.warn("要从缓存中查询中奖记录的key为空！");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
            if (!StringUtils.hasText(str)) {
                return Arrays.asList();
            }

            return JacksonUtil.readListValue(str, WinningRecordDO.class);
        } catch (Exception e) {

            log.error("从缓存中查询中奖记录异常！key:{}", WINNING_RECORDS_PREFIX + key);
            return Arrays.asList();
        }
    }
}